//
// Created by jack on 2021/2/6.
//

#include "weilapb_defines.h"

#include <WL.Location.pb.h>
#include <WL.Service.pb.h>
#include <pb_decode.h>
#include <pb_encode.h>

#include "weilapb_datafuns.h"

#include "weilapb_error.h"
#include "weilapb_internal.h"
#include "weilapb_location.h"

void* weilapb_decodeSubMsgLocationInfo(pb_istream_t* stream, const pb_field_t* field) {
    WL_Location_LocationInfo wlLocationLocationInfo = WL_Location_LocationInfo_init_zero;
    PBLinkItem*              pLinkItem              = PB_ALLOC(sizeof(PBLocationInfo) + sizeof(PBLinkItem));
    if (pLinkItem) {
        PBLocationInfo* pLocationInfo;
        pLinkItem->dataSize = sizeof(PBLocationInfo);
        pLocationInfo       = (PBLocationInfo*)pLinkItem->data;
        if (pb_decode(stream, WL_Location_LocationInfo_fields, &wlLocationLocationInfo)) {
            if (wlLocationLocationInfo.has_locationParam) {
                if (wlLocationLocationInfo.locationParam.has_longitude) {
                    pLocationInfo->longitude = wlLocationLocationInfo.locationParam.longitude;
                }

                if (wlLocationLocationInfo.locationParam.has_latitude) {
                    pLocationInfo->latitude = wlLocationLocationInfo.locationParam.latitude;
                }

                if (wlLocationLocationInfo.locationParam.has_speed) {
                    pLocationInfo->speed = wlLocationLocationInfo.locationParam.speed;
                }

                if (wlLocationLocationInfo.locationParam.has_direction) {
                    pLocationInfo->direction = wlLocationLocationInfo.locationParam.direction;
                }

                if (wlLocationLocationInfo.locationParam.has_radius) {
                    pLocationInfo->direction = wlLocationLocationInfo.locationParam.radius;
                }

                if (wlLocationLocationInfo.locationParam.has_altitude) {
                    pLocationInfo->altitude = wlLocationLocationInfo.locationParam.altitude;
                }

                if (wlLocationLocationInfo.locationParam.has_timestamp) {
                    pLocationInfo->timestamp = wlLocationLocationInfo.locationParam.timestamp;
                }
            }
        } else {
            PB_FREE(pLinkItem);
            pLinkItem = PB_NULL;
        }
    }

    return pLinkItem;
}

PBWeilaMsg* weilapb_decodeGetLocationRsp(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBGetLocationRsp));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PBFieldData       locationInfoField;
        PBGetLocationRsp* pGetLocationRsp;
        pWeilaMsg->contentSize = sizeof(PBGetLocationRsp);
        pGetLocationRsp        = (PBGetLocationRsp*)pWeilaMsg->pContent;

        weilapb_initDecDataItem(&locationInfoField, PB_DATA_TYPE_LINK_SUBMSG, 0, pGetLocationRsp);
        weilapb_setDecCallback(
            &locationInfoField, &serviceMessage.locationMessage.rspGetLocation.locationInfos, PB_NULL);
        locationInfoField.decSubMsg = weilapb_decodeSubMsgLocationInfo;

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            weilapb_addRecyclePointer(pWeilaMsg, RECYCLE_PTR_LINK, pGetLocationRsp);
            return pWeilaMsg;
        }

        weilapb_freePBLinkList(pGetLocationRsp);
        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeLocationCtrlNtf(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBLocationCtrlNtf));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PBLocationCtrlNtf* pPbLocationCtrlNtf;
        pWeilaMsg->contentSize = sizeof(PBLocationCtrlNtf);
        pPbLocationCtrlNtf     = (PBLocationCtrlNtf*)pWeilaMsg->pContent;
        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            if (serviceMessage.locationMessage.ntfLocationControl.has_sessionInfo) {
                if (serviceMessage.locationMessage.ntfLocationControl.sessionInfo.has_sessionId) {
                    pPbLocationCtrlNtf->sessionId =
                        serviceMessage.locationMessage.ntfLocationControl.sessionInfo.sessionId;
                }

                if (serviceMessage.locationMessage.ntfLocationControl.sessionInfo.has_sessionType) {
                    pPbLocationCtrlNtf->sessionType =
                        serviceMessage.locationMessage.ntfLocationControl.sessionInfo.sessionType;
                }
            }

            if (serviceMessage.locationMessage.ntfLocationControl.has_locationControl) {
                if (serviceMessage.locationMessage.ntfLocationControl.locationControl.has_frequency) {
                    pPbLocationCtrlNtf->frequency =
                        serviceMessage.locationMessage.ntfLocationControl.locationControl.frequency;
                }

                if (serviceMessage.locationMessage.ntfLocationControl.locationControl.has_duration) {
                    pPbLocationCtrlNtf->duration =
                        serviceMessage.locationMessage.ntfLocationControl.locationControl.duration;
                }

                if (serviceMessage.locationMessage.ntfLocationControl.locationControl.has_status) {
                    pPbLocationCtrlNtf->status =
                        serviceMessage.locationMessage.ntfLocationControl.locationControl.status;
                }
            }

            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg*
    weilapb_decodeLocationMsg(PB_INT32 commandId, PB_INT32 commandType, const PB_UINT8* pData, PB_UINT32 dataSize) {
    PBWeilaMsg* pPbWeilaMsg = PB_NULL;

    switch (commandId) {
        case WL_Location_LocationCommandId_LOCATION_COMMAND_REPORT_LOCATION: {
            pPbWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            if (pPbWeilaMsg) {
                pPbWeilaMsg->message = WL_LOCATION_REPORT_LOCATION_MSG;
            }
        } break;

        case WL_Location_LocationCommandId_LOCATION_COMMAND_LOCATION_CONTROL: {
            if (commandType == WL_Service_CommandType_COMMAND_RESPONSE) {
                pPbWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            } else {
                pPbWeilaMsg = weilapb_decodeLocationCtrlNtf(pData, dataSize);
            }

            if (pPbWeilaMsg) {
                pPbWeilaMsg->message = WL_LOCATION_LOCATION_CONTROL_MSG;
            }
        } break;

        case WL_Location_LocationCommandId_LOCATION_COMMAND_GET_LOCATION: {
            pPbWeilaMsg = weilapb_decodeGetLocationRsp(pData, dataSize);
            if (pPbWeilaMsg) {
                pPbWeilaMsg->message = WL_LOCATION_GET_LOCATION_MSG;
            }
        } break;

        case WL_Location_LocationCommandId_LOCATION_COMMAND_REPORT_TRACK_LOCATION: {
            pPbWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            if (pPbWeilaMsg) {
                pPbWeilaMsg->message = WL_LOCATION_REPORT_TRACK_LOCATION_MSG;
            }
        } break;

        case WL_Location_LocationCommandId_LOCATION_COMMAND_REPORT_BUSINESS_LOCATION: {
            pPbWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            if (pPbWeilaMsg) {
                pPbWeilaMsg->message = WL_LOCATION_REPORT_BUSINESS_LOCATION_MSG;
            }
        } break;
    }

    return pPbWeilaMsg;
}

// request

PBEncResult weilapb_buildLocationReq(PB_UINT16 commandId, WL_Location_LocationMessage* pParam) {
    PBEncResult                pbResult;
    WL_Service_ServiceMessage* pServiceMessage = weilapb_buildServiceMsg(
        WL_Service_ServiceID_SERVICE_LOCATION, commandId, WL_Service_CommandType_COMMAND_REQUEST);

    memset(&pbResult, 0, sizeof(PBEncResult));
    pbResult.message = weilapb_getMessageByCommandId(WL_Service_ServiceID_SERVICE_LOCATION, commandId);
    if (pServiceMessage) {
        pServiceMessage->has_locationMessage = true;
        memcpy(&pServiceMessage->locationMessage, pParam, sizeof(WL_Location_LocationMessage));
        if (weilapb_encodeWeilaMsg(pServiceMessage, &pbResult)) {
            PB_FREE(pServiceMessage);
            return pbResult;
        } else {
            PB_FREE(pServiceMessage);
            pbResult.errCode = ERR_ENCODE;
        }
    } else {
        pbResult.errCode = ERR_OUT_OF_MEMORY;
    }

    return pbResult;
}

static PB_BOOL weilapb_encodeSessionInfo(pb_ostream_t* stream, const pb_field_t* field, const void* pData) {
    WL_Location_LocationSessionInfo sessionInfo = WL_Location_LocationSessionInfo_init_zero;
    PBSessionInfo*                  pInfo       = (PBSessionInfo*)pData;

    sessionInfo.has_sessionId   = true;
    sessionInfo.has_sessionType = true;
    sessionInfo.sessionId       = pInfo->sessionId;
    sessionInfo.sessionType     = pInfo->sessionType;

    if (pb_encode_submessage(stream, WL_Location_LocationSessionInfo_fields, &sessionInfo)) {
        return PB_TRUE;
    }

    return PB_FALSE;
}

PBEncResult weilapb_buildReportLocationReq(PBSessionInfo*       pSessionInfoList,
                                           PB_UINT32            count,
                                           PBLocationInfo*      pLocationInfo,
                                           PBLocationExtraInfo* pExtraInfo) {
    PBFieldData                 extesionField;
    PBFieldData                 appIdField;
    PBFieldData                 sessionInfos;
    WL_Location_LocationMessage wlLocationLocationMessage = WL_Location_LocationMessage_init_zero;

    wlLocationLocationMessage.has_reqReportLocation = true;

    if (pExtraInfo) {
        wlLocationLocationMessage.reqReportLocation.has_extensionType = true;
        wlLocationLocationMessage.reqReportLocation.extensionType     = pExtraInfo->extensionType;

        weilapb_initEncDataItem(
            &extesionField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_LONG_STRING_LEN, pExtraInfo->extension);
        weilapb_setEncCallback(&extesionField, &wlLocationLocationMessage.reqReportLocation.extensionBody, PB_NULL);

        weilapb_initEncDataItem(&appIdField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_SHORT_STRING_LEN, pExtraInfo->appId);
        weilapb_setEncCallback(&appIdField, &wlLocationLocationMessage.reqReportLocation.appid, PB_NULL);
    }

    weilapb_initEncDataItem(&sessionInfos, PB_DATA_TYPE_ARRAY_SUBMSG, count, pSessionInfoList);
    weilapb_setEncCallback(&sessionInfos, &wlLocationLocationMessage.reqReportLocation.sessionInfos, PB_NULL);
    sessionInfos.encSubMsg  = weilapb_encodeSessionInfo;
    sessionInfos.subMsgSize = sizeof(PBSessionInfo);

    wlLocationLocationMessage.reqReportLocation.has_locationInfo = true;

    wlLocationLocationMessage.reqReportLocation.locationInfo.has_userId = true;
    wlLocationLocationMessage.reqReportLocation.locationInfo.userId     = pLocationInfo->userId;

    wlLocationLocationMessage.reqReportLocation.locationInfo.has_clientType = true;
    wlLocationLocationMessage.reqReportLocation.locationInfo.clientType     = pLocationInfo->clientType;

    wlLocationLocationMessage.reqReportLocation.locationInfo.has_locationType = true;
    wlLocationLocationMessage.reqReportLocation.locationInfo.locationType     = pLocationInfo->locationType;

    wlLocationLocationMessage.reqReportLocation.locationInfo.has_locationParam          = true;
    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.has_altitude = true;
    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.altitude     = pLocationInfo->altitude;

    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.has_latitude = true;
    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.latitude     = pLocationInfo->latitude;

    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.has_longitude = true;
    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.longitude     = pLocationInfo->longitude;

    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.has_direction = true;
    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.direction     = pLocationInfo->direction;

    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.has_radius = true;
    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.radius     = pLocationInfo->radius;

    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.has_speed = true;
    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.speed     = pLocationInfo->speed;

    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.has_timestamp = true;
    wlLocationLocationMessage.reqReportLocation.locationInfo.locationParam.timestamp     = pLocationInfo->timestamp;

    return weilapb_buildLocationReq(WL_Location_LocationCommandId_LOCATION_COMMAND_REPORT_LOCATION,
                                    &wlLocationLocationMessage);
}

PBEncResult weilapb_buildGetLocationReq(PB_UINT64 sessionId, PB_UINT16 sessionType) {
    WL_Location_LocationMessage wlLocationLocationMessage = WL_Location_LocationMessage_init_zero;

    wlLocationLocationMessage.has_reqGetLocation             = true;
    wlLocationLocationMessage.reqGetLocation.has_sessionInfo = true;

    wlLocationLocationMessage.reqGetLocation.sessionInfo.has_sessionId = true;
    wlLocationLocationMessage.reqGetLocation.sessionInfo.sessionId     = sessionId;

    wlLocationLocationMessage.reqGetLocation.sessionInfo.has_sessionType = true;
    wlLocationLocationMessage.reqGetLocation.sessionInfo.sessionType     = sessionType;

    return weilapb_buildLocationReq(WL_Location_LocationCommandId_LOCATION_COMMAND_GET_LOCATION,
                                    &wlLocationLocationMessage);
}

PBEncResult weilapb_buildLocationControlReq(PB_UINT64 sessionId,
                                            PB_UINT16 sessionType,
                                            PB_BOOL   status,
                                            PB_UINT32 frequency,
                                            PB_UINT32 duration) {
    WL_Location_LocationMessage wlLocationLocationMessage = WL_Location_LocationMessage_init_zero;

    wlLocationLocationMessage.has_reqLocationControl             = true;
    wlLocationLocationMessage.reqLocationControl.has_sessionInfo = true;

    wlLocationLocationMessage.reqLocationControl.sessionInfo.has_sessionId = true;
    wlLocationLocationMessage.reqLocationControl.sessionInfo.sessionId     = sessionId;

    wlLocationLocationMessage.reqLocationControl.sessionInfo.has_sessionType = true;
    wlLocationLocationMessage.reqLocationControl.sessionInfo.sessionType     = sessionType;

    wlLocationLocationMessage.reqLocationControl.has_locationControl        = true;
    wlLocationLocationMessage.reqLocationControl.locationControl.has_status = true;
    wlLocationLocationMessage.reqLocationControl.locationControl.status     = status;

    wlLocationLocationMessage.reqLocationControl.locationControl.has_duration = true;
    wlLocationLocationMessage.reqLocationControl.locationControl.duration     = duration;

    wlLocationLocationMessage.reqLocationControl.locationControl.has_frequency = true;
    wlLocationLocationMessage.reqLocationControl.locationControl.frequency     = frequency;

    return weilapb_buildLocationReq(WL_Location_LocationCommandId_LOCATION_COMMAND_LOCATION_CONTROL,
                                    &wlLocationLocationMessage);
}

PBEncResult weilapb_buildReportTrackLocationReq(PBLocationInfo* pLocationInfo) {
    WL_Location_LocationMessage wlLocationLocationMessage = WL_Location_LocationMessage_init_zero;

    wlLocationLocationMessage.has_reqReportTrackLocation              = true;
    wlLocationLocationMessage.reqReportTrackLocation.has_locationInfo = true;

    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.has_userId = true;
    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.userId     = pLocationInfo->userId;

    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.has_clientType = true;
    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.clientType     = pLocationInfo->clientType;

    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.has_locationType = true;
    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationType     = pLocationInfo->locationType;

    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.has_locationParam          = true;
    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.has_altitude = true;
    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.altitude     = pLocationInfo->altitude;

    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.has_latitude = true;
    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.latitude     = pLocationInfo->latitude;

    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.has_longitude = true;
    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.longitude = pLocationInfo->longitude;

    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.has_direction = true;
    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.direction = pLocationInfo->direction;

    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.has_radius = true;
    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.radius     = pLocationInfo->radius;

    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.has_speed = true;
    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.speed     = pLocationInfo->speed;

    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.has_timestamp = true;
    wlLocationLocationMessage.reqReportTrackLocation.locationInfo.locationParam.timestamp = pLocationInfo->timestamp;

    return weilapb_buildLocationReq(WL_Location_LocationCommandId_LOCATION_COMMAND_REPORT_TRACK_LOCATION,
                                    &wlLocationLocationMessage);
}

PBEncResult
    weilapb_buildReportBusinessLocationReq(PBLocationInfo* pLocationInfo, PB_UINT32 interval, PB_UINT32 serviceId) {
    WL_Location_LocationMessage wlLocationLocationMessage = WL_Location_LocationMessage_init_zero;

    wlLocationLocationMessage.has_reqReportBusinessLocation = true;

    wlLocationLocationMessage.reqReportBusinessLocation.has_interval = true;
    wlLocationLocationMessage.reqReportBusinessLocation.interval     = interval;
    if (serviceId > 0) {
        wlLocationLocationMessage.reqReportBusinessLocation.has_serviceId = true;
        wlLocationLocationMessage.reqReportBusinessLocation.serviceId     = serviceId;
    }
    wlLocationLocationMessage.reqReportBusinessLocation.has_locationInfo = true;

    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.has_userId = true;
    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.userId     = pLocationInfo->userId;

    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.has_clientType = true;
    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.clientType     = pLocationInfo->clientType;

    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.has_locationType = true;
    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationType     = pLocationInfo->locationType;

    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.has_locationParam          = true;
    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.has_altitude = true;
    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.altitude = pLocationInfo->altitude;

    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.has_latitude = true;
    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.latitude = pLocationInfo->latitude;

    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.has_longitude = true;
    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.longitude = pLocationInfo->longitude;

    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.has_direction = true;
    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.direction = pLocationInfo->direction;

    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.has_radius = true;
    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.radius     = pLocationInfo->radius;

    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.has_speed = true;
    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.speed     = pLocationInfo->speed;

    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.has_timestamp = true;
    wlLocationLocationMessage.reqReportBusinessLocation.locationInfo.locationParam.timestamp = pLocationInfo->timestamp;

    return weilapb_buildLocationReq(WL_Location_LocationCommandId_LOCATION_COMMAND_REPORT_BUSINESS_LOCATION,
                                    &wlLocationLocationMessage);
}